﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using FastReflectionLib;
using Fuse8.DomainFramework.DataAccess.SqlDatabase;
using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.Practices.Domain.Interfaces;
using Fuse8.DomainFramework.Practices.SystemFacade;
using Fuse8.DomainFramework.Practices.SystemFacade.Extensions;

namespace Fuse8.DomainFramework.Practices.DataAccess.SqlRepository
{
    public class SqlBaseMapper<TEntity, TKey> : BaseSqlEntityDataMapper<TEntity, TKey> where TEntity : class, IEntityObject<TKey>, new() where TKey : IComparable<TKey>, IEquatable<TKey>
    {
		private readonly SqlMultiMapperSettings<TEntity> _settings;

        internal  SqlMultiMapperSettings<TEntity> Settings
        {
            get { return _settings; }            
        }

        public SqlBaseMapper() {}

        public SqlBaseMapper(IList<string> ignoreToProperties, IList<string> ignoreFromProperties, SqlMultiMapperSettings<TEntity> settings = null) : base(ignoreToProperties, ignoreFromProperties)
        {
            _settings = settings;
        }

		internal bool IsMultiPropertiesAssigned<TParameter>()
		{
			return _settings != null && _settings.IsRegisteredForParameter<TParameter>();
		}

		#region BaseSqlEntityDataMapper override
		public override TEntity MapFromMulti(string sp, SqlDataReader reader, TEntity stepEntity, int index)
		{
			if (_settings == null)
			{
				throw new NullReferenceException("Mapper configuration for multi reader is not provded.");
			}

			return Mappers<TEntity, TEntity>.GetMulti(GetKey(sp, index), reader, index, stepEntity, _settings);
		}

        protected virtual object GetEntityForMatching(TEntity stepEntity)
        {
            return stepEntity;
        }

        private static void SetProperty(object entity, object value, PropertyInfo property)
    	{
    		if (!property.PropertyType.IsEnumerable())
    		{
    			property.FastSetValue(entity, value);
    		}
    		else
    		{
    			var collection = property.FastGetValue(entity) as IList;
    			var elementType = property.PropertyType.GetGenericArguments()[0];
    			if (collection == null)
    			{
    				collection = (IList) Activator.CreateInstance(typeof (List<>).MakeGenericType(elementType));
    				property.FastSetValue(entity, collection);
    			}
    			collection.Add(value);
    		}
    	}

    	public override TEntity MapFrom(string sp, SqlDataReader reader)
		{
			return MapFromReader<TEntity>(reader, sp, null);
		}

		#endregion
        
		protected virtual T MapFromReader<T> (SqlDataReader reader, string sp, int? index, TEntity stepEntity = null) where T : class
		{
			return Mappers<T, TEntity>.Get(GetKey(sp, index), reader, stepEntity);
		}

		private static string GetKey(string sp, int? index)
		{
			return String.Format("{0}_{1}", sp, index);
		}
    }
}